package com.store.orm.mysql.generator;

import com.store.orm.mysql.MySQLUtil;
import com.store.orm.mysql.annotation.*;
import com.store.orm.mysql.handler.ConvertTypeHandler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.lang.reflect.Field;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.List;
import java.util.Objects;

import static com.store.orm.common.KeyWord.*;

/**
 * @author 宫静雨
 * @version 1.0
 */
public class PojoToTable {

    private Connection connection;
    private MySQLUtil UTIL;
    private static final Logger logger = LoggerFactory.getLogger(PojoToTable.class);

    public PojoToTable(MySQLUtil UTIL) {
        this.UTIL = UTIL;
        this.connection = UTIL.getConnection();
    }

    public <T> void execute(Class<T> cls) {
        StringBuilder sb = new StringBuilder();
        String tableName = UTIL.getTableName(cls);
        sb.append(CREATE).append(TABLE).append(IF).append(NOT_EXISTS).append(tableName)
                .append(LEFT_ROUND_BRACKET).append(CRLF);
        Field[] fields = cls.getDeclaredFields();
        for (int i = 0; i < fields.length; i++) {
            sb.append(parseFieldAnnotation(fields[i]));
            if (i != fields.length - 1) {
                sb.append(COMMA).append(CRLF);
            }
        }
        sb.append(CRLF).append(RIGHT_ROUND_BRACKET).append(SEMICOLON);
        String sql = sb.toString();
        if (UTIL.isLogged()) {
            logger.info("{}", sql);
        }
        try {
            connection.setAutoCommit(false);
            connection.createStatement().execute(sql);
            connection.commit();
        } catch (SQLException e) {
            try {
                connection.rollback();
            } catch (SQLException ex) {
                logger.error("生成表时发生错误: {}", ex.getMessage());
            }
        }
    }

    public void execute(List<Class<?>> cls) {
        cls.forEach(this::execute);
    }

    private String parseFieldAnnotation(Field field) {
        StringBuilder sb = new StringBuilder();
        String fieldName = UTIL.getFieldName(field);
        if (field.isAnnotationPresent(TableField.class)) {
            TableField tableField = field.getAnnotation(TableField.class);
            String fieldType = tableField.type();
            sb.append(TAB).append(fieldName).append(BLANK).append(fieldType).append(BLANK);
        } else {
            Class<?> fieldType = field.getType();
            sb.append(TAB).append(fieldName).append(BLANK)
                    .append(ConvertTypeHandler.PT.get(fieldType)).append(BLANK);
        }

        if (field.isAnnotationPresent(References.class)) {
            String indexName = "idx_" + fieldName;
            sb.append(COMMA).append(CRLF).append(TAB).append(INDEX).append(BLANK).append(indexName)
                    .append(LEFT_ROUND_BRACKET).append(fieldName).append(RIGHT_ROUND_BRACKET);
        } else if (!field.isAnnotationPresent(ForeignKey.class)) {
            if (field.isAnnotationPresent(Id.class)) {
                sb.append(PRIMARY_KEY);
            }
            if (field.isAnnotationPresent(NotNull.class)) {
                sb.append(NOT_NULL);
            }
            if (field.isAnnotationPresent(Unique.class)) {
                sb.append(UNIQUE);
            }
            if (field.isAnnotationPresent(Default.class)) {
                Default aDefault = field.getAnnotation(Default.class);
                sb.append(DEFAULT).append(aDefault.value());
            }
            if (field.isAnnotationPresent(Comment.class)) {
                Comment comment = field.getAnnotation(Comment.class);
                sb.append(COMMENT).append(SINGLE_QUOTES).append(comment.value()).append(SINGLE_QUOTES).append(BLANK);
            }
        } else if (field.isAnnotationPresent(ForeignKey.class)) {
            ForeignKey foreignKey = field.getAnnotation(ForeignKey.class);
            Class<?> cls = foreignKey.fTable();
            String fkFieldName = null;
            for (Field fkField : cls.getDeclaredFields()) {
                if (fkField.isAnnotationPresent(References.class)) {
                    fkFieldName = UTIL.getFieldName(fkField);
                    break;
                }
            }
            if (Objects.isNull(fkFieldName)) {
                throw new RuntimeException("外键关联类的字段上没有 @References");
            }
            String fkTableName = UTIL.getTableName(cls);
            String foreignKeyName = fieldName + UNDERLINE + fkTableName + UNDERLINE + "fk";
            sb.append(COMMA).append(CRLF).append(TAB).append(FOREIGN_KEY).append(foreignKeyName)
                    .append(LEFT_ROUND_BRACKET).append(fieldName).append(RIGHT_ROUND_BRACKET).append(REFERENCES)
                    .append(fkTableName).append(LEFT_ROUND_BRACKET).append(fkFieldName).append(RIGHT_ROUND_BRACKET);
        }
        return sb.toString();
    }
}